
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/slab.h>

#include "chdev.h"
#include "moddefs.h"
#include "fileop.h"

extern int device_major;
extern int device_nums;

struct class *  signal_processing_class = NULL;

struct chdev_data device_data[ DEVICE_NUMS ] = {
		{ NULL, NULL, 0, NULL, NULL, 0 },
		{ NULL, NULL, 0, NULL, NULL, 0 },
		{ NULL, NULL, 0, NULL, NULL, 0 },
		{ NULL, NULL, 0, NULL, NULL, 0 }
};

dev_t devbase;

static const struct file_operations prince_fops = {
   .owner			= THIS_MODULE,
   .read			= fop_read,
   .write			= fop_write,
   .llseek			= fop_llseek,
   .open			= fop_open,
   .release			= fop_release,
   .mmap			= fop_mmap,
   .unlocked_ioctl	= fop_ioctl,
   .compat_ioctl	= fop_ioctl,
};

struct sw_encoder *  encoder;

static enum hrtimer_restart ktfunc( struct hrtimer* timer ) {
	struct sw_encoder* pEncoder = container_of( timer, struct sw_encoder, timer );
	ktime_t now = timer->base->get_time();
	hrtimer_forward( timer, now, pEncoder->interval );
	++pEncoder->azimuth;
	printk( KERN_NOTICE "%u\n", (unsigned short)(0x0FFF&pEncoder->azimuth) );
	return HRTIMER_RESTART;
}

int cdev_initialize()
{
	int	retval = 0;
	dev_t devnum;
	int i = 0;
	struct device * device;
	char devname[ 10 ];
	int length = 0;
	devbase = MKDEV( device_major, DEVICE_BASE_MINOR );
	if (device_major) {
		retval = register_chrdev_region( devbase, device_nums, THIS_MODULE->name );
	} else {
		retval = alloc_chrdev_region( &devbase, DEVICE_BASE_MINOR, device_nums, THIS_MODULE->name );
	}
	if (retval < 0) {
		device_major = -1;
		return retval;
	}/* else {
		printk( KERN_DEBUG "Success allocate device number region: %d:%d-%d:%d\n",
                MAJOR( devbase ), MINOR( devbase ),
                MAJOR( devbase ), MINOR( devbase ) + device_nums -1 );
	}*/
	device_major = MAJOR(devbase);
	signal_processing_class = class_create( THIS_MODULE, CLASS_NAME );
	if ( IS_ERR( signal_processing_class ) ) {
			retval = PTR_ERR( signal_processing_class );
			signal_processing_class = NULL;
			unregister_chrdev_region( devbase, device_nums );
			return retval;
	}
	for ( i = 0; i < DEVICE_NUMS; ++i ) {
		devnum = MKDEV( device_major, DEVICE_BASE_MINOR + i );
		device_data[ i ].cdev = cdev_alloc();
		cdev_init( device_data[ i ].cdev, &prince_fops );
		device_data[ i ].cdev->owner = THIS_MODULE;
		retval = cdev_add( device_data[ i ].cdev, devnum, 1 );
		if (retval) {
			cdev_del( device_data[ i ].cdev );
			device_data[ i ].cdev = NULL;
			device_data[ i ].device = NULL;
			printk( KERN_DEBUG "Error cdev_add for %d: %d\n", devnum, retval );
			continue;
		} else {
			printk ( KERN_DEBUG "Successful created: %d:%d\n", MAJOR(devnum), MINOR(devnum) );
		}
		length = snprintf( devname, 10, "%s%d", DEVICE_NAME, i );
		devname[ length ] = '\0';
		device = device_create( signal_processing_class, NULL, devnum, NULL, devname );
		if ( IS_ERR(device) ) {
			cdev_del( device_data[ i ].cdev );
			device_data[ i ].cdev = NULL;
			device_data[ i ].device = NULL;
		} else {
			device_data[ i ].device = device;
			device_data[ i ].page = (unsigned char *) __get_free_page( GFP_KERNEL );
			memset( (void*)device_data[ i ].page, (int)'A' + i, PAGE_SIZE );
		}
	}
	encoder = kmalloc( sizeof(*encoder), GFP_KERNEL );
	encoder->interval = ktime_set( 0, 1220703 );
	encoder->azimuth = 0;
	hrtimer_init( &encoder->timer, CLOCK_REALTIME, HRTIMER_MODE_REL );
	encoder->timer.function = ktfunc;
	return retval;
}

void cdev_deinitialize()
{
	int i = 0;
	dev_t devnum;
	if ( hrtimer_active(&encoder->timer) ) {
		hrtimer_cancel(&encoder->timer);
	}
	kfree( encoder );
	for ( i = 0; i < DEVICE_NUMS; ++i ) {
		devnum = MKDEV( device_major, DEVICE_BASE_MINOR + i);
		if ( device_data[ i ].device ) {
			free_page( (unsigned long) device_data[ i ].page );
			device_destroy( signal_processing_class, devnum );
			cdev_del( device_data[ i ].cdev );
			device_data[ i ].cdev = NULL;
		}
	}
	if (signal_processing_class) {
		class_destroy( signal_processing_class );
	}
	unregister_chrdev_region( devbase, device_nums );
}
